              package com.java.diagnostics.visualizer.impl.properties;
              
              import com.java.diagnostics.visualizer.data.DataSet;
              import com.java.diagnostics.visualizer.data.TupleData;
              import com.java.diagnostics.visualizer.data.Variant;
              import com.java.diagnostics.visualizer.data.axes.Axis;
              import com.java.diagnostics.visualizer.data.axes.AxisPair;
              import com.java.diagnostics.visualizer.data.axes.XAxis;
              import com.java.diagnostics.visualizer.data.axes.XDataAxis;
              import com.java.diagnostics.visualizer.data.axes.YAxis;
              import com.java.diagnostics.visualizer.data.axes.YDataAxis;
              import com.java.diagnostics.visualizer.impl.GCAndMemoryVisualizerImplPreferenceInitalizer;
              import com.java.diagnostics.visualizer.impl.axes.AxisUnits;
              import com.java.diagnostics.visualizer.impl.factory.LogFactory;
              import com.java.diagnostics.visualizer.impl.preferences.DataSetScope;
              import com.java.diagnostics.visualizer.impl.preferences.GCAndMemoryVisualizerPreferenceInitializer;
              import com.java.diagnostics.visualizer.impl.preferences.PreferencesImportListener;
              import com.java.diagnostics.visualizer.impl.preferences.SmartPreferences;
              import com.java.diagnostics.visualizer.impl.properties.ids.StructuredIDsToDisplayPropertiesImpl;
              import com.java.diagnostics.visualizer.impl.properties.ids.TupleIDsToDisplayPropertiesImpl;
              import com.java.diagnostics.visualizer.impl.properties.ids.UnstructuredIDsToDisplayPropertiesImpl;
              import com.java.diagnostics.visualizer.impl.util.weak.WeakHashSet;
              import com.java.diagnostics.visualizer.properties.IDsToDisplayProperties;
              import com.java.diagnostics.visualizer.properties.OutputProperties;
              import com.java.diagnostics.visualizer.properties.OutputPropertiesChangedEvent;
              import com.java.diagnostics.visualizer.properties.OutputPropertiesListener;
              import com.java.diagnostics.visualizer.properties.ZoomListener;

              import java.util.HashMap;
              import java.util.HashSet;
              import java.util.Hashtable;
              import java.util.Iterator;
              import java.util.Map;
              import java.util.Set;
              import java.util.TreeMap;
              import java.util.logging.Logger; 

import org.eclipse.core.runtime.preferences.IScopeContext;
import org.eclipse.core.runtime.preferences.InstanceScope;
              //import org.eclipse.core.runtime.preferences.IScopeContext;
              //import org.eclipse.core.runtime.preferences.InstanceScope;
              
              public class OutputPropertiesImpl
                implements OutputProperties, PreferencesImportListener
              {
                private static final String CONSTRUCTOR = "constructor";
                private static final Logger TRACE = LogFactory.getTrace(PreferencesImportListener.class);
              
                private final String CLASSNAME = getClass().getName();
                private IDsToDisplayProperties tupleFieldsToDisplay;
                private IDsToDisplayProperties structuredFieldsToDisplay;
                private IDsToDisplayProperties unstructuredFieldsToDisplay;
                private Map<String, Object> displayerProperties;
                private Map<String, Double> minimumX;
                private Map<String, Double> maximumX;
                private Map<String, Double> minimumY;
                private Map<String, Double> maximumY;
                private boolean notificationsEnabled = true;
                private boolean croppingChanged = false;
                private boolean unitsChanged = false;
              
                private boolean notificationQueued = false;
                private final IScopeContext scope;
                private SmartPreferences preferences;
                private static final String AXIS_UNITS_KEY = "_axis.units_";
                private static final String EMPTY_STRING = "";
                private final String axisUnitPreferencePrefix = this.CLASSNAME + "_axis.units_";
                private Set<OutputPropertiesListener> listeners;
                private Hashtable<String, YAxis> yAxisCache;
                private XAxis xAxis;
                private Integer saveHeight = null;
                private Integer saveWidth = null;
                private DataSet dataSet;
                private double xZoomStart = 0.0D;
                private double xZoomEnd = 1.0D;
                private double yZoomStart = 0.0D;
                private double yZoomEnd = 1.0D;
              
                private Set<ZoomListener> zoomListeners = new HashSet();
              
                private HashMap<Axis, String> axisToUnits = new HashMap();
                private HashMap<Axis, String> axisToInitialUnits = new HashMap();
              
                private boolean zoomChanged = false;
              
                public OutputPropertiesImpl(DataSet dataSet)
                {
                  TRACE.entering(this.CLASSNAME, "constructor");
                  this.dataSet = dataSet;
                  this.displayerProperties = new HashMap();
              
                  if (dataSet != null) {
                    this.scope = new DataSetScope(dataSet);
                  }
                  else {
                    this.scope = new InstanceScope();
                  }
                  this.preferences = 
                    GCAndMemoryVisualizerImplPreferenceInitalizer.getInstance().getPreferences(this.scope, this.scope);
              
                  if (dataSet != null) {
                    this.preferences.addPreferenceListener(this);
                  }
              
                  this.yAxisCache = new Hashtable();
                  this.xAxis = null;
                  this.listeners = new WeakHashSet();
              
                  this.minimumX = new TreeMap();
                  this.maximumX = new TreeMap();
                  this.minimumY = new TreeMap();
                  this.maximumY = new TreeMap();
              
                  initialiseFieldsToDisplay();
              
                  TRACE.exiting(this.CLASSNAME, "constructor");
                }
              
                public DataSet getDataSet()
                {
                  return this.dataSet;
                }
              
                private void initialiseFieldsToDisplay() {
                  this.tupleFieldsToDisplay = new TupleIDsToDisplayPropertiesImpl(this.scope, this);
                  this.structuredFieldsToDisplay = new StructuredIDsToDisplayPropertiesImpl(
                    this.scope, this);
                  this.unstructuredFieldsToDisplay = new UnstructuredIDsToDisplayPropertiesImpl(
                    this.scope, this);
                }
              
                public Object getDisplayerProperties(String displayerId)
                {
                  return this.displayerProperties.get(displayerId);
                }
              
                public void setDisplayerProperties(String displayerId, Object object) {
                  this.displayerProperties.put(displayerId, object);
                }
              
                public IDsToDisplayProperties getTupleFieldsToDisplay() {
                  return this.tupleFieldsToDisplay;
                }
              
                public IDsToDisplayProperties getStructuredFieldsToDisplay() {
                  return this.structuredFieldsToDisplay;
                }
              
                public IDsToDisplayProperties getUnstructuredFieldsToDisplay() {
                  return this.unstructuredFieldsToDisplay;
                }
              
                public void setMinimumX(String units, Double value)
                {
                  Double currentValue = getMinimumX(units);
                  if (((currentValue == null) && (value != null)) || (
                    (currentValue != null) && (!currentValue.equals(value)))) {
                    this.minimumX.put(units, value);
                    notifyCroppingChanged();
                  }
                }
              
                public void setMaximumX(String units, Double value)
                {
                  Double currentValue = getMaximumX(units);
                  if (((currentValue == null) && (value != null)) || (
                    (currentValue != null) && (!currentValue.equals(value)))) {
                    this.maximumX.put(units, value);
                    notifyCroppingChanged();
                  }
                }
              
                public void setMinimumY(String units, Double value)
                {
                  Double currentValue = getMinimumY(units);
                  if (((currentValue == null) && (value != null)) || (
                    (currentValue != null) && (!currentValue.equals(value)))) {
                    this.minimumY.put(units, value);
                    notifyCroppingChanged();
                  }
                }
              
                public void setMaximumY(String units, Double value)
                {
                  Double currentValue = getMaximumY(units);
                  if (((currentValue == null) && (value != null)) || (
                    (currentValue != null) && (!currentValue.equals(value)))) {
                    this.maximumY.put(units, value);
                    notifyCroppingChanged();
                  }
                }
              
                public Double getMinimumX(String units)
                {
                  return getDataPointFromMap(units, this.minimumX);
                }
              
                public Double getMaximumX(String units)
                {
                  return getDataPointFromMap(units, this.maximumX);
                }
              
                public Double getMinimumY(String units)
                {
                  return getDataPointFromMap(units, this.minimumY);
                }
              
                public Double getMaximumY(String units)
                {
                  return getDataPointFromMap(units, this.maximumY);
                }
              
                private final Double getDataPointFromMap(String units, Map<String, Double> map)
                {
                  Object value = map.get(units);
                  return (Double)value;
                }
              
                public void clearMaximumX()
                {
                  if (!this.maximumX.isEmpty()) {
                    this.maximumX.clear();
                    notifyCroppingChanged();
                  }
                }
              
                public void clearMaximumY()
                {
                  if (!this.maximumY.isEmpty()) {
                    this.maximumY.clear();
                    notifyCroppingChanged();
                  }
                }
              
                public void clearMinimumX()
                {
                  if (!this.minimumX.isEmpty()) {
                    this.minimumX.clear();
                    notifyCroppingChanged();
                  }
                }
              
                public void clearMinimumY()
                {
                  if (!this.minimumY.isEmpty()) {
                    this.minimumY.clear();
                    notifyCroppingChanged();
                  }
                }
              
                public void addListener(OutputPropertiesListener listener) {
                  this.listeners.add(listener);
                }
              
                public void removeListener(OutputPropertiesListener listener) {
                  this.listeners.remove(listener);
                }
              
                public void disableNotifications() {
                  this.notificationsEnabled = false;
                }
              
                public void enableNotifications() {
                  this.notificationsEnabled = true;
                  if ((this.croppingChanged) || (this.unitsChanged) || (this.notificationQueued)) {
                    notifyListeners();
                  }
                  if (this.zoomChanged)
                    notifyZoomListeners();
                }
              
                private void notifyCroppingChanged()
                {
                  if (this.notificationsEnabled) {
                    this.croppingChanged = false;
                    notifyListeners();
                  } else {
                    this.croppingChanged = true;
                  }
                }
              
                private void notifyUnitsChanged() {
                  if (this.notificationsEnabled) {
                    this.unitsChanged = false;
                    notifyListeners();
                  } else {
                    this.unitsChanged = true;
                  }
                }
              
                public void notifyListeners() {
                  if (this.notificationsEnabled)
                    notifyListeners(new OutputPropertiesChangedEvent(this));
                  else
                    this.notificationQueued = true;
                }
              
                private void notifyListeners(OutputPropertiesChangedEvent event)
                {
                  if (this.notificationsEnabled)
                  {
                    this.croppingChanged = false;
                    this.unitsChanged = false;
                    this.notificationQueued = false;
              
                    Iterator it = this.listeners.iterator();
                    while (it.hasNext()) {
                      OutputPropertiesListener listener = (OutputPropertiesListener)it.next();
                      listener.outputPropertiesChanged(event);
                    }
                  }
                }
              
                public XAxis getXAxis() {
                  return this.xAxis;
                }
              
                public void setXAxis(XAxis axis) {
                  this.xAxis = axis;
                }
              
                public YAxis getYAxis(String name) {
                  return (YAxis)this.yAxisCache.get(name);
                }
              
                public void setYAxis(String name, YAxis axis) {
                  this.yAxisCache.put(name, axis);
                }
              
                public double getXZoomStart() {
                  return this.xZoomStart;
                }
              
                public double getXZoomEnd() {
                  return this.xZoomEnd;
                }
              
                public double getYZoomStart() {
                  return this.yZoomStart;
                }
              
                public double getYZoomEnd() {
                  return this.yZoomEnd;
                }
              
                public void setZoomLimits(double xZoomStart, double xZoomEnd, double yZoomStart, double yZoomEnd)
                {
                  if ((xZoomStart != this.xZoomStart) || (xZoomEnd != this.xZoomEnd) || 
                    (yZoomStart != this.yZoomStart) || (yZoomEnd != this.yZoomEnd))
                  {
                    this.xZoomStart = Math.max(0.0D, Math.min(xZoomStart, 1.0D));
                    this.xZoomEnd = Math.max(0.0D, Math.min(xZoomEnd, 1.0D));
                    this.yZoomStart = Math.max(0.0D, Math.min(yZoomStart, 1.0D));
                    this.yZoomEnd = Math.max(0.0D, Math.min(yZoomEnd, 1.0D));
                    notifyZoomListeners();
                  }
                }
              
                public void notifyZoomListeners()
                {
                  if (this.notificationsEnabled)
                  {
                    this.zoomChanged = false;
              
                    Iterator it = this.zoomListeners.iterator();
                    while (it.hasNext()) {
                      ZoomListener listener = (ZoomListener)it.next();
                      listener.zoomChanged();
                    }
                  } else {
                    this.zoomChanged = true;
                  }
                }
              
                public void addZoomListener(ZoomListener listener)
                {
                  this.zoomListeners.add(listener);
                }
              
                public void removeZoomListener(ZoomListener listener) {
                  this.zoomListeners.remove(listener);
                }
              
                public void setSaveHeight(Integer height) {
                  this.saveHeight = height;
                }
              
                public void setSaveWidth(Integer width) {
                  this.saveWidth = width;
                }
              
                public Integer getSaveHeight() {
                  return this.saveHeight;
                }
              
                public Integer getSaveWidth() {
                  return this.saveWidth;
                }
              
                public void setUnits(Axis axis, String units)
                {
                  String unitsSet = (String)this.axisToUnits.get(axis);
                  if (((unitsSet != null) && (!unitsSet.equals(units))) || (
                    (unitsSet == null) && (units != null))) {
                    setAxisUnits(axis, units);
                    notifyUnitsChanged();
                  }
                }
              
                private void setAxisUnits(Axis axis, String units) {
                  this.axisToUnits.put(axis, units);
              
              // deleted by wenguang.huang 
/* 520 *///          setUnitsPreference(axis);
                }
              
                public String getUnits(Axis axis)
                {
                  String units = (String)this.axisToUnits.get(axis);
                  if (units != null) {
                    return units;
                  }
              
                  units = (String)this.axisToInitialUnits.get(axis);
                  if (units != null) {
                    return units;
                  }
              
                  return axis.getBaseUnitName();
                }
              
                public String getXUnits(TupleData tuple) {
                  return getUnits(tuple.getAxisPair().getXAxis().getAxis());
                }
              
                public String getYUnits(TupleData tuple) {
                  return getUnits(tuple.getAxisPair().getYAxis().getAxis());
                }
              
                public void setDefaultUnits(Axis axis, String units)
                {
                  String unitsToSet = getPreferredUnits(axis);
                  if (unitsToSet.length() == 0) {
                    unitsToSet = units;
                  }
              
                  this.axisToInitialUnits.put(axis, unitsToSet);
                  setAxisUnits(axis, null);
                }
              
                private void setUnitsPreference(Axis axis)
                {
                  String unitKey = AxisUnits.getPreferenceKey(getUnits(axis));
                  this.preferences.setValue(getAxisUnitPreferenceName(axis), unitKey);
                }
              
                private String getAxisUnitPreferenceName(Axis axis) {
                  return this.axisUnitPreferencePrefix + axis.getID();
                }
              
                public void preferenceChange()
                {
                  disableNotifications();
                  for (Axis axis : this.axisToUnits.keySet())
                  {
                    String preferenceUnits = getPreferredUnits(axis);
                    if ((preferenceUnits.length() > 0) && 
                      (!preferenceUnits.equals(getUnits(axis)))) {
                      this.axisToUnits.put(axis, preferenceUnits);
                    }
              
                  }
              
                  for (Axis axis : this.axisToUnits.keySet()) {
                    this.axisToInitialUnits.put(axis, getUnits(axis));
                  }
              
                  enableNotifications();
                }
              
                private String getPreferredUnits(Axis axis) {
                  String preferredUnits = this.preferences
                    .getString(getAxisUnitPreferenceName(axis));
                  if (preferredUnits.length() > 0) {
                    preferredUnits = AxisUnits.getUnits(preferredUnits);
                    if (!validateUnitSetting(axis, preferredUnits)) {
                      preferredUnits = "";
                    }
                  }
                  return preferredUnits;
                }
              
                private boolean validateUnitSetting(Axis axis, String units)
                {
                  if (this.dataSet == null) {
                    return true;
                  }
              
                  for (Variant v : this.dataSet.getVariants()) {
                    for (TupleData tuple : v.getTupleData()) {
                      if ((tuple.getXAxis().getAxis() == axis) && 
                        (!tuple.getXAxis().isPossibleUnit(units))) {
                        return false;
                      }
                      if ((tuple.getYAxis().getAxis() == axis) && 
                        (!tuple.getYAxis().isPossibleUnit(units))) {
                        return false;
                      }
                    }
                  }
              
                  return true;
                }
              }

